package com.teamide.util;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.security.MessageDigest;
import java.security.SecureRandom;
import java.util.Base64;

public class EncryptUtil {
	private static final String MD5 = "MD5";
	private static final String SHA1 = "SHA1";
	private static final String HmacMD5 = "HmacMD5";
	private static final String HmacSHA1 = "HmacSHA1";
	private static final String DES = "DES";
	private static final String AES = "AES";

	/** 编码格式；默认使用uft-8 */
	private static final String charset = "utf-8";
	/** DES */
	private static final int keysizeDES = 0;
	/** AES */
	private static final int keysizeAES = 128;

	private static EncryptUtil me;

	private EncryptUtil() {
		// 单例
	}

	// 双重锁
	public static EncryptUtil getInstance() {
		if (me == null) {
			synchronized (EncryptUtil.class) {
				if (me == null) {
					me = new EncryptUtil();
				}
			}
		}
		return me;
	}

	/**
	 * 使用MessageDigest进行单向加密（无密码）
	 * 
	 * @param res
	 *            被加密的文本
	 * @param algorithm
	 *            加密算法名称
	 * @return
	 */
	private static String messageDigest(String res, String algorithm) {
		try {
			MessageDigest md = MessageDigest.getInstance(algorithm);
			byte[] resBytes = charset == null ? res.getBytes() : res.getBytes(charset);
			return base64(md.digest(resBytes));
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

	/**
	 * 使用KeyGenerator进行单向/双向加密（可设密码）
	 * 
	 * @param res
	 *            被加密的原文
	 * @param algorithm
	 *            加密使用的算法名称
	 * @param key
	 *            加密使用的秘钥
	 * @return
	 */
	private static String keyGeneratorMac(String res, String algorithm, String key) {
		try {
			SecretKey sk = null;
			if (key == null) {
				KeyGenerator kg = KeyGenerator.getInstance(algorithm);
				sk = kg.generateKey();
			} else {
				byte[] keyBytes = charset == null ? key.getBytes() : key.getBytes(charset);
				sk = new SecretKeySpec(keyBytes, algorithm);
			}
			Mac mac = Mac.getInstance(algorithm);
			mac.init(sk);
			byte[] result = mac.doFinal(res.getBytes());
			return base64(result);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

	private static String base64(byte[] res) {
		return Base64.getEncoder().encodeToString(res);
	}

	/**
	 * 使用KeyGenerator双向加密，DES/AES，注意这里转化为字符串的时候是将2进制转为16进制格式的字符串，不是直接转，因为会出错
	 * 
	 * @param res
	 *            加密的原文
	 * @param algorithm
	 *            加密使用的算法名称
	 * @param key
	 *            加密的秘钥
	 * @param keysize
	 * @param isEncode
	 * @return
	 */
	private static String keyGeneratorES(String res, String algorithm, String key, int keysize, boolean isEncode) {
		try {
			KeyGenerator kg = KeyGenerator.getInstance(algorithm);
			if (keysize == 0) {
				byte[] keyBytes = charset == null ? key.getBytes() : key.getBytes(charset);
				kg.init(new SecureRandom(keyBytes));
			} else if (key == null) {
				kg.init(keysize);
			} else {
				byte[] keyBytes = charset == null ? key.getBytes() : key.getBytes(charset);
				// 防止linux下 随机生成key
				SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");
				secureRandom.setSeed(keyBytes);
				kg.init(128, secureRandom);
			}
			SecretKey sk = kg.generateKey();
			SecretKeySpec sks = new SecretKeySpec(sk.getEncoded(), algorithm);
			Cipher cipher = Cipher.getInstance(algorithm);
			if (isEncode) {
				cipher.init(Cipher.ENCRYPT_MODE, sks);
				byte[] resBytes = charset == null ? res.getBytes() : res.getBytes(charset);
				return new String(Base64.getEncoder().encode(cipher.doFinal(resBytes)));
			} else {
				cipher.init(Cipher.DECRYPT_MODE, sks);
				byte[] resBytes = charset == null ? res.getBytes() : res.getBytes(charset);
				return new String(cipher.doFinal(Base64.getDecoder().decode(resBytes)));
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

	/**
	 * md5加密算法进行加密（不可逆）
	 * 
	 * @param res
	 *            需要加密的原文
	 * @return
	 */
	public static String MD5(String res) {
		return messageDigest(res, MD5);
	}

	/**
	 * md5加密算法进行加密（不可逆）
	 * 
	 * @param res
	 *            需要加密的原文
	 * @param key
	 *            秘钥
	 * @return
	 */
	public static String MD5(String res, String key) {
		return keyGeneratorMac(res, HmacMD5, key);
	}

	/**
	 * 使用SHA1加密算法进行加密（不可逆）
	 * 
	 * @param res
	 *            需要加密的原文
	 * @return
	 */
	public static String SHA1(String res) {
		return messageDigest(res, SHA1);
	}

	/**
	 * 使用SHA1加密算法进行加密（不可逆）
	 * 
	 * @param res
	 *            需要加密的原文
	 * @param key
	 *            秘钥
	 * @return
	 */
	public static String SHA1(String res, String key) {
		return keyGeneratorMac(res, HmacSHA1, key);
	}

	/**
	 * 使用DES加密算法进行加密（可逆）
	 * 
	 * @param res
	 *            需要加密的原文
	 * @param key
	 *            秘钥
	 * @return
	 */
	public static String DESencode(String res, String key) {
		return keyGeneratorES(res, DES, key, keysizeDES, true);
	}

	/**
	 * 对使用DES加密算法的密文进行解密（可逆）
	 * 
	 * @param res
	 *            需要解密的密文
	 * @param key
	 *            秘钥
	 * @return
	 */
	public static String DESdecode(String res, String key) {
		return keyGeneratorES(res, DES, key, keysizeDES, false);
	}

	/**
	 * 使用AES加密算法经行加密（可逆）
	 * 
	 * @param res
	 *            需要加密的密文
	 * @param key
	 *            秘钥
	 * @return
	 */
	public static String AESencode(String res, String key) {
		return keyGeneratorES(res, AES, key, keysizeAES, true);
	}

	/**
	 * 对使用AES加密算法的密文进行解密
	 * 
	 * @param res
	 *            需要解密的密文
	 * @param key
	 *            秘钥
	 * @return
	 */
	public static String AESdecode(String res, String key) {
		return keyGeneratorES(res, AES, key, keysizeAES, false);
	}

	/**
	 * 使用Base64进行加密
	 * 
	 * @param res
	 *            密文
	 * @return
	 */
	public static String Base64Encode(String res) {
		return Base64.getEncoder().encodeToString(res.getBytes());
	}

	/**
	 * 使用Base64进行解密
	 * 
	 * @param res
	 * @return
	 */
	public static String Base64Decode(String res) {
		return new String(Base64.getDecoder().decode(res));
	}
}